﻿namespace Attacher
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Windows.Forms;
	using System.Windows.Input;
	using System.Windows.Interop;

	using Attacher.HotKeys;
	using Attacher.VisualStudio;

	using EnvDTE;

	using EnvDTE80;

	internal class AttacherService : IAttacherService
	{
		#region Attributes

		private readonly IHotKeyService hotKeyService;
		private readonly IVisualStudioInstanceService visualStudioInstanceService;
		private readonly ISelectInstanceWindowViewModelFactory selectInstanceWindowViewModelFactory;
		private readonly IExceptionReportingService exceptionReportingService;

		private HotKey hotKey;

		#endregion

		#region Public Methods

		public AttacherService(IHotKeyService hotKeyService, IVisualStudioInstanceService visualStudioInstanceService, ISelectInstanceWindowViewModelFactory selectInstanceWindowViewModelFactory, IExceptionReportingService exceptionReportingService)
		{
			ParameterChecker.IsNotNull(hotKeyService, "hotKeyService");
			ParameterChecker.IsNotNull(visualStudioInstanceService, "visualStudioInstanceService");
			ParameterChecker.IsNotNull(selectInstanceWindowViewModelFactory, "selectInstanceWindowViewModelFactory");
			ParameterChecker.IsNotNull(exceptionReportingService, "exceptionReportingService");

			this.hotKeyService = hotKeyService;
			this.visualStudioInstanceService = visualStudioInstanceService;
			this.selectInstanceWindowViewModelFactory = selectInstanceWindowViewModelFactory;
			this.exceptionReportingService = exceptionReportingService;
		}

		public void Enable()
		{
			this.hotKey = new HotKey(Keys.A, ModifierKeys.Control | ModifierKeys.Alt); // TODO: Make this dynamic
			
			this.hotKeyService.RegisterHotKey(this.hotKey, this.OnAttachRequested);
		}

		public void Disable()
		{
			this.hotKeyService.UnRegisterHotKey(this.hotKey);
		}

		#endregion

		#region Event Handlers

		private void OnAttachRequested()
		{
			uint processId;

			IntPtr foregroundHandler = NativeMethods.GetForegroundWindow();
			NativeMethods.GetWindowThreadProcessId(foregroundHandler, out processId);

			IEnumerable<VisualStudioInstance> instances = this.visualStudioInstanceService.GetInstances();

			int numberOfInstances = instances.Count();

			if (numberOfInstances > 1)
			{
				SelectInstanceWindow window = new SelectInstanceWindow();
				SelectInstanceWindowViewModel viewModel = this.selectInstanceWindowViewModelFactory.Create(instances);

				// The the currently focused window as the parent of the dialog
				WindowInteropHelper windowInteropHelper = new WindowInteropHelper(window);
				windowInteropHelper.Owner = foregroundHandler;

				window.DataContext = viewModel;

				bool? dialogResult = window.ShowDialog();

				if (dialogResult != null && dialogResult.Value)
				{
					DTE instance = viewModel.SelectedInstance.Model.Instance;
					this.AttachProcessToDte(instance, processId);
				}
			}
			else if (numberOfInstances == 1)
			{
				DTE instance = instances.ToArray()[0].Instance;
				this.AttachProcessToDte(instance, processId);
			}
		}


		#endregion

		#region Private Methods

		private void AttachProcessToDte(DTE instance, uint processId)
		{
			Process process = instance.Debugger.LocalProcesses.OfType<Process>().Single(p => p.ProcessID == processId);
			Process2 newProcess = process as Process2;

			if (newProcess != null)
			{
				try
				{
					newProcess.Attach2("Managed");
				}
				catch (Exception ex)
				{
					// Can fail due to multiple factors. Must be gracefully handled.
					this.exceptionReportingService.ReportException(ex);
				}
			}
			else
			{
				try
				{
					process.Attach();
				}
				catch (Exception ex)
				{
					// Can fail due to multiple factors. Must be gracefully handled.
					this.exceptionReportingService.ReportException(ex);
				}
			}
		}

		#endregion
	}
}
